package com.xu.cloud.common.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Properties;

/**
 * Druid数据源配置类
 *
 * @author xuguan
 * @since 2023/7/12
 */
@Configuration
@ConditionalOnClass({
        DruidDataSource.class
})
@ConditionalOnProperty(prefix = "spring.datasource", name = "type", havingValue = "com.alibaba.druid.pool.DruidDataSource")
@ConditionalOnExpression(value = "'com.alibaba.druid.pool.DruidDataSource'.equals('${spring.datasource.type}')")
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class, DruidDataSourceAutoConfigure.class})
@SuppressWarnings({"rawtypes", "unchecked"})
public class DruidDataSourceConfig {

    @Bean
    @ConditionalOnMissingBean
    @ConfigurationProperties("spring.datasource.druid")
    public DataSource druidDataSource(DataSourceProperties properties) {
        try (DruidDataSource druidDataSource = properties
                .initializeDataSourceBuilder()
                .type(DruidDataSource.class)
                .build()) {

            // 连接池参数配置
            // 初始化大小, 最小, 最大
            druidDataSource.setInitialSize(5);
            druidDataSource.setMinIdle(5);
            druidDataSource.setMaxActive(20);
            // 获取连接等待超时时间, 单位是毫秒
            druidDataSource.setMaxWait(60000);
            // 间隔多久进行一次检测, 检测需要关闭的空闲连接, 单位是毫秒
            druidDataSource.setMaxWait(60000);
            // 一个连接在池中最小生存的时间, 单位是毫秒
            druidDataSource.setMinEvictableIdleTimeMillis(300000);
            druidDataSource.setValidationQuery("SELECT 1");
            druidDataSource.setTestWhileIdle(true);
            druidDataSource.setTestOnBorrow(false);
            druidDataSource.setTestOnReturn(false);
            // 打开PSCache, 并且指定每个连接上PSCache的大小
            druidDataSource.setPoolPreparedStatements(true);
            druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(20);

            // 配置监控统计拦截的filters，去掉后监控界面sql无法统计，wall用于防火墙
            druidDataSource.setFilters("stat,wall,slf4j");
            druidDataSource.setUseGlobalDataSourceStat(true);
            // 通过connectProperties属性来打开mergeSql功能; 慢SQL记录
            Properties prop = new Properties();
            prop.put("druid.stat.mergeSql", "true");
            prop.put("druid.stat.slowSqlMillis", "500");
            druidDataSource.setConnectProperties(prop);
            return druidDataSource;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 配置监控服务器
     *
     * @return 返回监控注册的servlet对象
     */
    @Bean
    public ServletRegistrationBean statViewServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        // 添加IP白名单
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
        // 添加IP黑名单，当白名单和黑名单重复时，黑名单优先级更高
        // servletRegistrationBean.addInitParameter("deny", "127.0.0.1");
        // 添加控制台管理用户
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "admin");
        // 是否能够重置数据
        servletRegistrationBean.addInitParameter("resetEnable", "true");
        return servletRegistrationBean;
    }

    /**
     * 配置服务过滤器
     *
     * @return 返回过滤器配置对象
     */
    @Bean
    public FilterRegistrationBean statFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        // 添加过滤规则
        filterRegistrationBean.addUrlPatterns("/*");
        // 忽略过滤格式
        filterRegistrationBean.addInitParameter("exclusions", "*.html,*.css,*.js,*.gif,*.jpg,*.png,*.ico,/druid/*,");
        return filterRegistrationBean;
    }
}
